#include "dicom/gateway.h"

#include <surbasic/surbasic.h>
#include <gwbase/base/logging.h>
#include <gwbase/base/string_util.h>
#include <gwbase/base/pidfile.h>

#include "dicom/hippa_log.h"
#include "dicom/handler/login_handler.h"
#include "dicom/handler/file_upload_handler.h"
#include "dicom/handler/file_download_handler.h"
#include "dicom/handler/lookup_handler.h"
#include "dicom/handler/readdir_handler.h"
#include "dicom/handler/file_op_handler.h"
#include "dicom/handler/dir_op_handler.h"
#include "dicom/handler/statfs_handler.h"

namespace dicom {

static int SurbasicLogCallback(SDLogLevel level, const char* fmt, ...) {
  if (static_cast<int>(level) < static_cast<int>(GetLogLevel())) {
    return 0;
  }

  std::string result;
  va_list ap;
  va_start(ap, fmt);
  StringPrintfV(&result, fmt, ap);
  va_end(ap);

  if (result.size() == 0 || result[result.size() - 1] != '\n')
    result.append("\n");

  const char* kPrefix = "SURCLOUD: ";
  const size_t kPrefixLen = strlen(kPrefix);
  GetCurrentSink()->WriteLog(kPrefix, kPrefixLen);
  GetCurrentSink()->WriteLog(result.c_str(), result.size());
  GetCurrentSink()->Flush();
  return 0;
}

Gateway::Gateway()
    : conf_(NULL),
      resume_info_db_(NULL),
      cache_(NULL),
      put_sink_pool_(NULL),
      session_mgr_(NULL),
      sync_mgr_(NULL),
      dmv_mgr_(NULL),
      web_svr_(NULL),
      hcache_(NULL) {}

Gateway::~Gateway() {
  delete hcache_;
  delete web_svr_;
  delete dmv_mgr_;
  delete sync_mgr_;
  delete session_mgr_;
  delete put_sink_pool_;
  delete cache_;
  delete resume_info_db_;
  delete conf_;
}

bool Gateway::LoadConfAndInit(const std::string& conf_file, bool daemon) {
  std::string dbdir;
  std::string datadir;
  std::string resumedbPath;
  Status s;

  SDBasic::log_callback = SurbasicLogCallback;
  SDBasic::Init();

  conf_ = DicomConf::LoadConfFile(conf_file);
  if (conf_ == NULL) {
    LOG_ERROR << "faile to parse config file: " << conf_file;
    goto cleanup;
  }

  if (daemon) {
    std::string logdir = conf_->GetLogDir();
    dicom::SetRollingFileLogging(logdir, "dicom");
  }

  InitHippaLog(conf_);

  // init working dir
  dbdir = conf_->GetDbDir();
  s = dicom::CreateDirRecurisve(dbdir);
  if (!s.ok()) {
    LOG_ERROR << "failed to create dir: " << dbdir;
    goto cleanup;
  }

  datadir = conf_->GetDataDir();
  s = dicom::CreateDirRecurisve(datadir);
  if (!s.ok()) {
    LOG_ERROR << "failed to create dir: " << datadir;
    goto cleanup;
  }

  // resume info db
  resume_info_db_ = new ResumeInfoDb();
  resumedbPath = path::Join(conf_->GetDbDir(), "resume_info.db");
  s = resume_info_db_->OpenDb(resumedbPath);
  if (!s.ok()) {
    goto cleanup;
  }

  // session manager
  session_mgr_ = NewSessionManager(conf_);
  if (session_mgr_ == NULL) goto cleanup;

  // init sync manager & dmv db
  sync_mgr_ = NewSyncManager(conf_);
  if (sync_mgr_ == NULL) goto cleanup;

  dmv_mgr_ = NewDmvDBManager(conf_);
  if (dmv_mgr_ == NULL) goto cleanup;

  // init local storage
  cache_ = NewLocalCache(datadir, conf_->GetMaxStorageSpace());
  if (cache_ == NULL) {
    goto cleanup;
  }

  put_sink_pool_ = new PutSinkPool(cache_);

  web_svr_ = new WebServer(this, conf_);

  hcache_ = OpenHashCache(conf_);
  if (hcache_ == NULL) {
    goto cleanup;
  }

  return true;

cleanup:
  delete hcache_;
  delete web_svr_;
  delete dmv_mgr_;
  delete sync_mgr_;
  delete session_mgr_;
  delete put_sink_pool_;
  delete cache_;
  return false;
}

void Gateway::Start() {
  sync_mgr_->LoadPendingTasks();
  sync_mgr_->StartWorkers(conf_->GetUploadThreads());
  session_mgr_->StartTokenRefresher();

  LoginHandler::RegisterHandler(web_svr_, conf_);
  FileUploadHandler::RegisterHandler(web_svr_);
  FileDownloadHandler::RegisterHandler(web_svr_);
  LookupHandler::RegisterHandler(web_svr_);
  ReaddirHandler::RegisterHandler(web_svr_);
  FileOpHandler::RegisterHandler(web_svr_);
  DirOpHandler::RegisterHandler(web_svr_);
  StatfsHandler::RegisterHandler(web_svr_, conf_);

  CreatePidFile(conf_->GetPidFile());

  web_svr_->Start();
}

static Gateway* g_instance = NULL;
void SetGateway(Gateway* gw) {
  assert(g_instance == NULL);
  g_instance = gw;
}

Gateway* GetGateway() {
  assert(g_instance != NULL);
  return g_instance;
}
}
